package com.paradogs.framework.core.exceptions;

import com.paradogs.framework.core.annoations.exception.PRExceptionHandler;
import com.paradogs.framework.core.annoations.exception.PRExceptionHandlerClass;
import com.paradogs.framework.core.utils.SpringUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

/**
 * @author: yumi
 * @date: 2023/8/2  9:12
 * @Description: TODO
 */
@Slf4j
@Component
public class GlobalExceptionDispatch {

    @Getter
    private Map<Class<? extends Exception>, Method> exceptionHandlerMap = new HashMap<>();

    /**
     * 加载注册异常处理
     */
    @PostConstruct
    public void registerExceptionHandler() {
        for (Object bean : SpringUtils.getBeansWithAnnotation(PRExceptionHandlerClass.class).values()) {
            for (Method method : bean.getClass().getDeclaredMethods()) {
                PRExceptionHandler PRExceptionHandler = method.getAnnotation(PRExceptionHandler.class);
                if (PRExceptionHandler == null) {
                    continue;
                }
                exceptionHandlerMap.put(PRExceptionHandler.value(), method);
            }
        }

    }

    /**
     * 将异常转发到对应异常处理方法
     * ps: 会自动优先找最相近的类
     * @param throwable
     */
    public void forward(Throwable throwable) {

        if (throwable == null) {return;}

        Throwable realThrowable = throwable.getCause() != null ? throwable.getCause() : throwable;

        // 这里的异常时 InvocationTargetException 反射异常，转换成真正的异常
        Method method = exceptionHandlerMap.get(realThrowable.getClass());
        if (method == null && (method = exceptionHandlerMap.get(Exception.class)) == null) {
            realThrowable.printStackTrace();
            return;
        }
        try {
            method.invoke(
                    SpringUtils.getBean(method.getDeclaringClass()),
                    Stream.of(method.getParameterTypes())
                            .map(type -> Exception.class.isAssignableFrom(type) ? realThrowable : null)
                            .toArray()
            );
        } catch (Exception e) {
            throw new RuntimeException(e);
//            // 可能会死循环
//            forward(e.getCause());
        }
    }


}
